package com.ar4j.bench.hibernate.service;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Service;

import com.ar4j.bench.common.domain.IItem;
import com.ar4j.bench.common.domain.IProduct;
import com.ar4j.bench.common.domain.ProductWithItems;
import com.ar4j.bench.common.service.IProductService;
import com.ar4j.bench.hibernate.domain.Item;
import com.ar4j.bench.hibernate.domain.Product;

/**
 * A product service implemented using the Hibernate library
 */
@Service
public class ProductHibernateService extends HibernateDaoSupport implements IProductService {

  @Autowired
  public void setAutowiredSessionFactory(SessionFactory factory) {
    super.setSessionFactory(factory);
  }

  @Override
  public Long addProduct(IProduct product) {
    getHibernateTemplate().save(product);
    return product.getId();
  }

  @Override
  public Long addProductWithItems(ProductWithItems product) {
    Product prod = (Product) product.getProduct();
    
    Set<Item> itemSet = new HashSet<Item>();
    for(IItem item : product.getItems()) {
      Item cur = (Item) item;
      cur.setProduct(prod);
      itemSet.add(cur);
    }
    
    prod.setItems(itemSet);
    
    getHibernateTemplate().save(prod);
    return prod.getId();
  }

  @SuppressWarnings("unchecked")
  @Override
  public List<IProduct> findByCategoryId(Long categoryId) {
    return getHibernateTemplate().find("from Product p where p.category.id = ?", categoryId);
  }

  @Override
  public IProduct findById(Long id) {
    return getHibernateTemplate().get(Product.class, id);
  }

  @Override
  public List<ProductWithItems> findProductWithItemsByCategoryId(final Long categoryId) {
    return getHibernateTemplate().execute(new HibernateCallback<List<ProductWithItems>>() {
      
      @SuppressWarnings("unchecked")
      @Override
      public List<ProductWithItems> doInHibernate(Session session) throws HibernateException, SQLException {
        
        Query qry = session.createQuery("from Product p where p.category.id = ?");
        qry.setLong(1, categoryId);
        List<Product> results = (List<Product>) qry.list();
        
        List<ProductWithItems> out = new ArrayList<ProductWithItems>();
        for(Product product : results) {
          ProductWithItems withItems = new ProductWithItems();
          withItems.setProduct(product);
          withItems.setItems(new ArrayList<IItem>(product.getItems()));
          
          out.add(withItems);
        }
        
        return out;
      }
    });
  }

  @Override
  public ProductWithItems findProductWithItemsById(final Long id) {
    return getHibernateTemplate().execute(new HibernateCallback<ProductWithItems>() {
      @Override
      public ProductWithItems doInHibernate(Session session) throws HibernateException, SQLException {
        Product product = (Product) session.get(Product.class, id);
        
        ProductWithItems withItems = new ProductWithItems();
        withItems.setProduct(product);
        withItems.setItems(new ArrayList<IItem>(product.getItems()));
        
        return withItems;
      }
    });
  }

  @Override
  public void removeById(Long id) {
    getHibernateTemplate().delete(findById(id));    
  }

  @Override
  public void removeProductWithItemsById(Long id) {
    removeById(id);
  }

  @Override
  public void updateProduct(IProduct product) {
    getHibernateTemplate().update(product);    
  }

  @Override
  public void updateProductWithItems(final ProductWithItems product) {
    getHibernateTemplate().execute(new HibernateCallback<Object>() {
    
      @Override
      public Object doInHibernate(Session session) throws HibernateException, SQLException {
        Product prod = (Product) product.getProduct();
    
        Set<Item> itemSet = new HashSet<Item>();
        for(IItem item : product.getItems()) {
          Item cur = (Item) item;
          cur.setProduct(prod);
          itemSet.add(cur);
        }
        
        prod.setItems(itemSet);
        session.saveOrUpdate(prod);
        
        return null;
      }
    });
  }
  
}
